<!DOCTYPE html>



  


<html class="theme-next gemini use-motion" lang="zh-CN">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"/>
<meta name="theme-color" content="#222">












<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />






















<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=6.0.6" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=6.0.6">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=6.0.6">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=6.0.6">


  <link rel="mask-icon" href="/images/logo.svg?v=6.0.6" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '6.0.6',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  

<meta name="description" content="IO多路复用IO多路复用就是我们经常说的select epoll.select和epoll，好处是单个process就可以同时处理多个网络IO。基本原理是select\epoll会不断的轮询所负责的所有socket，当有某个socket数据到达了，就通知用户进程。  在同一个线程里面，接收多个连接，select/epoll会把他们都监视起来，然后像拨开关一样，谁有数据就拨向谁，然后调用相应的代码处">
<meta name="keywords" content="异步,selectors,事件驱动">
<meta property="og:type" content="article">
<meta property="og:title" content="IO多路复用模块selectors">
<meta property="og:url" content="http://7ht.gitee.io/2018/03/26/IO多路复用模块selectors/index.html">
<meta property="og:site_name" content="HonTu&#39;s Notes">
<meta property="og:description" content="IO多路复用IO多路复用就是我们经常说的select epoll.select和epoll，好处是单个process就可以同时处理多个网络IO。基本原理是select\epoll会不断的轮询所负责的所有socket，当有某个socket数据到达了，就通知用户进程。  在同一个线程里面，接收多个连接，select/epoll会把他们都监视起来，然后像拨开关一样，谁有数据就拨向谁，然后调用相应的代码处">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="http://p5rqvdp4f.bkt.clouddn.com/18-3-26/34234579.jpg">
<meta property="og:updated_time" content="2018-03-27T01:53:33.195Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="IO多路复用模块selectors">
<meta name="twitter:description" content="IO多路复用IO多路复用就是我们经常说的select epoll.select和epoll，好处是单个process就可以同时处理多个网络IO。基本原理是select\epoll会不断的轮询所负责的所有socket，当有某个socket数据到达了，就通知用户进程。  在同一个线程里面，接收多个连接，select/epoll会把他们都监视起来，然后像拨开关一样，谁有数据就拨向谁，然后调用相应的代码处">
<meta name="twitter:image" content="http://p5rqvdp4f.bkt.clouddn.com/18-3-26/34234579.jpg">






  <link rel="canonical" href="http://7ht.gitee.io/2018/03/26/IO多路复用模块selectors/"/>



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>IO多路复用模块selectors | HonTu's Notes</title>
  









  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"> 

<div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">HonTu's Notes</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">一小瓶</p>
      
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        
          
  <li class="menu-item menu-item-home">
    <a href="/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页</a>
</li>

      
        
        
          
  <li class="menu-item menu-item-about">
    <a href="/about/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于</a>
</li>

      
        
        
          
  <li class="menu-item menu-item-tags">
    <a href="/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />标签</a>
</li>

      
        
        
          
  <li class="menu-item menu-item-categories">
    <a href="/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br />分类</a>
</li>

      

      
    </ul>
  

  

  
</nav>


  



 </div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
            

          
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://7ht.gitee.io/2018/03/26/IO多路复用模块selectors/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Q.hongtao">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/head_cat.jpg">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="HonTu's Notes">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">IO多路复用模块selectors</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2018-03-26T15:43:05+08:00">2018-03-26</time>
            

            
            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Python/" itemprop="url" rel="index"><span itemprop="name">Python</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          
            <span class="post-meta-divider">|</span>
            <span class="post-meta-item-icon"
            >
            <i class="fa fa-eye"></i>
             阅读次数： 
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>
            </span>
          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <h1 id="IO多路复用"><a href="#IO多路复用" class="headerlink" title="IO多路复用"></a>IO多路复用</h1><p>IO多路复用就是我们经常说的select epoll.select和epoll，好处是单个process就可以同时处理多个网络IO。基本原理是select\epoll会不断的轮询所负责的所有socket，当有某个socket数据到达了，就通知用户进程。</p>
<blockquote>
<p>在同一个线程里面，接收多个连接，select/epoll会把他们都监视起来，然后像拨开关一样，谁有数据就拨向谁，然后调用相应的代码处理。</p>
</blockquote>
<p><img src="http://p5rqvdp4f.bkt.clouddn.com/18-3-26/34234579.jpg" alt=""></p>
<blockquote>
<p>当用户进程调用了select，那么整个进程会被block，而同时，kernel会“监视”所有select负责的socket，当任何一个socket中的数据准备好了，select就会返回。这个时候用户进程再调用read操作，将数据从kernel拷贝到用户进程。</p>
</blockquote>
<p>文件描述符fd:</p>
<blockquote>
<p>为打开文件的文件描述符，而每个进程都有一张文件描述符表，fd文件描述符就是这张表的索引，同样这张表中有一表项，该表项又是指向前面提到打开文件的file结构体，file结构体才是内核中用来描述文件属性的结构体。<br>我们都知道在Linux下一切皆文件。当然设备也不例外，如果要对某个设备进行操作，就不得不打开此设备文件，打开文件就会获得该文件的文件描述符fd( file discriptor), 它就是一个很小的整数。</p>
</blockquote>
<p>IO多路复用：</p>
<blockquote>
<p>事件循环不断地调用select获取被激光的socket，然后根据获取socket对应的EventHandler，执行Handle_event函数即可。<br>因为select()会阻塞，因此只能被称为异步阻塞IO，而非真正的异步IO；</p>
</blockquote>
<p>asyncio协程：</p>
<blockquote>
<p>根据事件驱动写的异步框架；</p>
</blockquote>
<p>个人总结：</p>
<ol>
<li>将不阻塞（原阻塞）的方法，注册进IO事件循环里—手动写；</li>
<li>外部loop，调用select()阻塞loop，等待被调用（然后执行具体逻辑操作）—手动写；</li>
<li>内部kernel不停地轮询注册进IO事件的文件对象，一旦有文件对象调用，则触发2里的select()，执行下一步（本质就是往select阻塞的那个队列里塞了个数据）—内部自动，看不见；</li>
</ol>
<h1 id="Selectors主要对象"><a href="#Selectors主要对象" class="headerlink" title="Selectors主要对象"></a>Selectors主要对象</h1><p>类的层次结构<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">BaseSelector</span><br><span class="line">+-- SelectSelector</span><br><span class="line">+-- PollSelector</span><br><span class="line">+-- EpollSelector</span><br><span class="line">+-- DevpollSelector</span><br><span class="line">+-- KqueueSelector</span><br></pre></td></tr></table></figure></p>
<p>在下文中，<strong>事件</strong> 是指那些等待I/O 事件的给定的文件对象的位掩码（可读/可写）。它可以是下面的模块常量的组合︰<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">EVENT_READ	 可读</span><br><span class="line">EVENT_WRITE	可写</span><br></pre></td></tr></table></figure></p>
<p>SelectorKey —<strong>文件对象+文件描述符fd，可读/可写事件，文件对象相关联的回调函数</strong></p>
<blockquote>
<p>是一个用来将文件对象关联到其底层文件描述符，选定的事件掩码和附加的数据的 namedtuple。它由 BaseSelector 的几种方法返回。</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">fileobj</span><br><span class="line">注册的文件对象。</span><br><span class="line"></span><br><span class="line">fd</span><br><span class="line">底层的文件描述符。</span><br><span class="line"></span><br><span class="line">events</span><br><span class="line">该文件对象必须等待的事件。</span><br><span class="line"></span><br><span class="line">data</span><br><span class="line">可选的与此文件对象相关联的不透明数据︰ 例如，这可以用来存储每个客户端的会话 id</span><br></pre></td></tr></table></figure>
<hr>
<p>BaseSelector —<strong>多个文件对象的IO事件 的操作选择器类，返回类型是SelectorKey实例</strong></p>
<ul>
<li>register(fileobj, events, data=None) —<strong>注册文件对象、可读/可写、回调函数，data就是回调函数</strong><blockquote>
<p>注册一个文件对象到选择器来监视它的 I/O 事件</p>
</blockquote>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">fileobj 是要监视的文件对象。它可能是一个整型文件描述符或有 fileno() 方法的对象。events 是要监视的事件的位掩码。data 是不透明的对象。</span><br><span class="line"></span><br><span class="line">这返回一个新的 SelectorKey 实例，或者因无效的事件掩码或文件描述符抛出 ValueError 错误，或者如果文件对象已注册则抛出 KeyError 错误。</span><br></pre></td></tr></table></figure>
<ul>
<li>unregister(fileobj) —<strong>注销文件对象</strong><blockquote>
<p>从选择器注销一个文件对象并移除对它的监视。文件对象被注销前应先关闭</p>
</blockquote>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">fileobj 必须是先前注册过的文件对象。</span><br><span class="line">这返回关联的 SelectorKey 实例，或者如果 fileobj未注册则抛出 KeyError错误。如果 fileobj 无效则抛出ValueError错误（例如，它没有 fileno() 方法或其 fileno() 方法有无效的返回值）。</span><br></pre></td></tr></table></figure>
<ul>
<li>select(timeout=None) —<strong>阻塞，等待触发事件列表里 文件对象被触发，返回文件对象列表</strong><blockquote>
<p>等到一些已注册的文件对象准备好或者超时。<br>如果 timeout &gt; 0，会指定最长的等待时间，以秒为单位。如果 timeout &lt; = 0，调用不会阻塞并会报告目前准备好的文件对象。如果 timeout 是 None，调用将会阻塞直到一个监视的文件对象准备好。</p>
</blockquote>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">这将返回一个以 (key, events) 元组为元素的列表，每一个元组代表一个准备好的文件对象。</span><br><span class="line"></span><br><span class="line">key 是对应于一个准备好的文件对象的SelectorKey 实例。events 是该文件对象上准备好的事件位掩码。</span><br></pre></td></tr></table></figure>
<h1 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h1><p>TCP Server</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line">import selectors</span><br><span class="line">import socket</span><br><span class="line"></span><br><span class="line"># 默认选择器类型select/epoll，返回一个选择器；用来操作IO事件</span><br><span class="line">sel = selectors.DefaultSelector()</span><br><span class="line">sock = socket.socket()</span><br><span class="line">sock.bind((&apos;127.0.0.1&apos;, 8800))</span><br><span class="line">sock.listen(5)</span><br><span class="line">sock.setblocking(False)  # 设置socket为不阻塞</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">##############################################################</span><br><span class="line"># 子socket的handle处理方法--回调方法</span><br><span class="line"># conn--文件对象子socket，mask--文件对象的事件（可读/可写）</span><br><span class="line">##############################################################</span><br><span class="line">def read(conn, mask):</span><br><span class="line">    try:</span><br><span class="line">        data = conn.recv(1024)</span><br><span class="line">        print(data.decode(&apos;utf-8&apos;))</span><br><span class="line">        print(&quot;接收到：&#123;&#125;&quot;.format(data))</span><br><span class="line">        data2 = &quot;xxxxxxx&quot;</span><br><span class="line">        print(&quot;发送数据：&#123;&#125;&quot;.format(data2))</span><br><span class="line">        conn.send(data2.encode(&quot;utf-8&quot;))</span><br><span class="line">    except Exception as e:</span><br><span class="line">		# 注销掉事件：client断开时，出发异常将该子socket文件对象从IO事件列表移除（注销）</span><br><span class="line">        sel.unregister(conn)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">##############################################################</span><br><span class="line"># 主socket的handle方法--回调方法，主要aceept接收socket客户端，</span><br><span class="line"># so--文件对象主socket，mask--文件对象的事件（可读/可写）</span><br><span class="line">##############################################################</span><br><span class="line"></span><br><span class="line">def accept(so, mask):</span><br><span class="line">    conn, addr = so.accept()</span><br><span class="line">    print(&quot;-----------&quot;, conn)</span><br><span class="line">	# 注册子socket文件对象，事件，回调函数（上面的read方法）</span><br><span class="line">    sel.register(conn, selectors.EVENT_READ, read)</span><br><span class="line"></span><br><span class="line"># 注册主socket事件，事件（可读/可写），回调函数</span><br><span class="line">sel.register(sock, selectors.EVENT_READ, accept)</span><br><span class="line">while 1:</span><br><span class="line">    print(&quot;waiting.....&quot;)</span><br><span class="line">	# 阻塞，等待任意一个文件对象被触发，返回文件对象列表</span><br><span class="line">	events = sel.select()</span><br><span class="line"></span><br><span class="line">    for key, mask in events:</span><br><span class="line">		&apos;&apos;&apos;</span><br><span class="line">		遍历获得   被触发文件对象，并通过获取文件对象关联的回调方法，执行回调；</span><br><span class="line">		在这里可以进行过滤，判断不同的文件对象，是否进行不同的操作；</span><br><span class="line">		&apos;&apos;&apos;</span><br><span class="line">        print(key.fileobj) # 文件对象</span><br><span class="line">        print(key.fd) # 文件对象 关联的 文件描述符fd</span><br><span class="line">        func = key.data  # 文件对象关联的回调函数</span><br><span class="line">        obj = key.fileobj  # 文件对象</span><br><span class="line">        func(obj, mask)   # 执行回调方法</span><br></pre></td></tr></table></figure>
<hr>
<h1 id="IO模型（课外阅读）"><a href="#IO模型（课外阅读）" class="headerlink" title="IO模型（课外阅读）"></a>IO模型（课外阅读）</h1><p>IO 多路复用是5种I/O模型中的第3种，对各种模型讲个故事，描述下区别：<br>故事情节为：老李去买火车票，三天后买到一张退票。参演人员（老李，黄牛，售票员，快递员），往返车站耗费1小时。</p>
<ol>
<li><p>阻塞I/O模型<br>老李去火车站买票，排队三天买到一张退票。<br>耗费：在车站吃喝拉撒睡 3天，其他事一件没干。</p>
</li>
<li><p>非阻塞I/O模型<br>老李去火车站买票，隔12小时去火车站问有没有退票，三天后买到一张票。<br>耗费：往返车站6次，路上6小时，其他时间做了好多事。</p>
</li>
<li><p>I/O复用模型</p>
<ul>
<li>select/poll<br>老李去火车站买票，委托黄牛，然后每隔6小时电话黄牛询问，黄牛三天内买到票，然后老李去火车站交钱领票。<br>耗费：往返车站2次，路上2小时，黄牛手续费100元，打电话17次</li>
<li>epoll<br>老李去火车站买票，委托黄牛，黄牛买到后即通知老李去领，然后老李去火车站交钱领票。<br>耗费：往返车站2次，路上2小时，黄牛手续费100元，无需打电话</li>
<li><p>区别</p>
<ul>
<li><p>1、epoll内部使用了mmap共享了用户和内核的部分空间，避免了数据的来回拷贝；</p>
</li>
<li><p>2、epoll基于事件驱动，epoll_ctl注册事件并注册callback回调函数，epoll_wait只返回发生的事件避免了像select和poll对事件的整个轮寻操作。nginx中使用了epoll，是基于事件驱动模型的，由一个或多个事件收集器来收集或者分发事件，epoll就属于事件驱动模型的事件收集器，将注册过的事件中发生的事件收集起来，master进程负责管理worker进程。</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>信号驱动I/O模型<br>老李去火车站买票，给售票员留下电话，有票后，售票员电话通知老李，然后老李去火车站交钱领票。<br>耗费：往返车站2次，路上2小时，免黄牛费100元，无需打电话</p>
</li>
<li><p>异步I/O模型<br>老李去火车站买票，给售票员留下电话，有票后，售票员电话通知老李并快递送票上门。<br>耗费：往返车站1次，路上1小时，免黄牛费100元，无需打电话</p>
</li>
</ol>
<p>1同2的区别是：自己轮询<br>2同3的区别是：委托黄牛<br>3同4的区别是：电话代替黄牛<br>4同5的区别是：电话通知是自取还是送票上门</p>
<h1 id="事件驱动模型（课外阅读）"><a href="#事件驱动模型（课外阅读）" class="headerlink" title="事件驱动模型（课外阅读）"></a>事件驱动模型（课外阅读）</h1><p>在UI编程中，常常要对鼠标点击进行相应，首先如何获得鼠标点击呢？<br>方式一：创建一个线程，该线程一直循环检测是否有鼠标点击，那么这个方式有以下几个缺点：</p>
<ol>
<li>CPU资源浪费，可能鼠标点击的频率非常小，但是扫描线程还是会一直循环检测，这会造成很多的CPU资源浪费；如果扫描鼠标点击的接口是阻塞的呢？</li>
<li>如果是堵塞的，又会出现下面这样的问题，如果我们不但要扫描鼠标点击，还要扫描键盘是否按下，由于扫描鼠标时被堵塞了，那么可能永远不会去扫描键盘；</li>
<li>如果一个循环需要扫描的设备非常多，这又会引来响应时间的问题；<br>所以，该方式是非常不好的。</li>
</ol>
<p>方式二：就是事件驱动模型<br>目前大部分的UI编程都是事件驱动模型，如很多UI平台都会提供onClick()事件，这个事件就代表鼠标按下事件。事件驱动模型大体思路如下：</p>
<ol>
<li>有一个事件（消息）队列；</li>
<li>鼠标按下时，往这个队列中增加一个点击事件（消息）；</li>
<li><strong>有个循环，不断从队列取出事件，根据不同的事件，调用不同的函数，如onClick()、onKeyDown()等；</strong></li>
<li>事件（消息）一般都各自保存各自的处理函数指针，这样，每个消息都有独立的处理函数；</li>
</ol>
<p>事件驱动编程是一种编程范式，这里程序的执行流由外部事件来决定。它的特点是包含一个事件循环，当外部事件发生时使用回调机制来触发相应的处理。另外两种常见的编程范式是（单线程）同步以及多线程编程。</p>

      
    </div>

    

    
    
    

    <div>
          
            
<div class="my_post_copyright">
  <script src="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script>

  <!-- JS库 sweetalert 可修改路径 -->
  <script src="https://cdn.bootcss.com/jquery/2.0.0/jquery.min.js"></script>
  <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
  <p><span>本文标题:</span><a href="/2018/03/26/IO多路复用模块selectors/">IO多路复用模块selectors</a></p>
  <p><span>文章作者:</span><a href="/" title="访问 Q.hongtao 的个人博客">Q.hongtao</a></p>
  <p><span>发布时间:</span>2018年03月26日 - 15:03</p>
  <p><span>最后更新:</span>2018年03月27日 - 09:03</p>
  <p><span>原始链接:</span><a href="/2018/03/26/IO多路复用模块selectors/" title="IO多路复用模块selectors">http://7ht.gitee.io/2018/03/26/IO多路复用模块selectors/</a>
    <span class="copy-path"  title="点击复制文章链接"><i class="fa fa-clipboard" data-clipboard-text="http://7ht.gitee.io/2018/03/26/IO多路复用模块selectors/"  aria-label="复制成功！"></i></span>
  </p>
  <p><span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>
</div>
<script>
    var clipboard = new Clipboard('.fa-clipboard');
      $(".fa-clipboard").click(function(){
      clipboard.on('success', function(){
        swal({
          title: "",
          text: '复制成功',
          icon: "success",
          showConfirmButton: true
          });
        });
    });
</script>


          
      </div>

    

    

    
  <div>
  
    

  
</div>
    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/异步/" rel="tag"># 异步</a>
          
            <a href="/tags/selectors/" rel="tag"># selectors</a>
          
            <a href="/tags/事件驱动/" rel="tag"># 事件驱动</a>
          
        </div>
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2018/03/25/Python基础总结（转）/" rel="next" title="Python基础总结（转）">
                <i class="fa fa-chevron-left"></i> Python基础总结（转）
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2018/03/27/python基础回顾（一）/" rel="prev" title="python基础回顾（一）">
                python基础回顾（一） <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/images/head_cat.jpg"
                alt="Q.hongtao" />
            
              <p class="site-author-name" itemprop="name">Q.hongtao</p>
              <p class="site-description motion-element" itemprop="description">没有可怕的深度 就没有美丽的水面</p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/archives">
                
                    <span class="site-state-item-count">34</span>
                    <span class="site-state-item-name">日志</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">9</span>
                    <span class="site-state-item-name">分类</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">42</span>
                    <span class="site-state-item-name">标签</span>
                  </a>
                </div>
              
            </nav>
          

          

          
            <div class="links-of-author motion-element">
              
                <span class="links-of-author-item">
                  <a href="https://github.com/ht-dep" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i>GitHub</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="mailto:15662728521@163.com" target="_blank" title="E-Mail"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  
                </span>
              
            </div>
          

          
          

          
          

          
            
          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#IO多路复用"><span class="nav-number">1.</span> <span class="nav-text">IO多路复用</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Selectors主要对象"><span class="nav-number">2.</span> <span class="nav-text">Selectors主要对象</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#代码实现"><span class="nav-number">3.</span> <span class="nav-text">代码实现</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#IO模型（课外阅读）"><span class="nav-number">4.</span> <span class="nav-text">IO模型（课外阅读）</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#事件驱动模型（课外阅读）"><span class="nav-number">5.</span> <span class="nav-text">事件驱动模型（课外阅读）</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>
<div class="copyright">&copy; 2017 &mdash; <span itemprop="copyrightYear">2019</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">ht-dep</span>

  

  
</div>



















        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv" title="总访客量">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
    </span>
  

  
    <span class="site-pv" title="总访问量">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
    </span>
  
</div>









        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>


























  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=6.0.6"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=6.0.6"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=6.0.6"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=6.0.6"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=6.0.6"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=6.0.6"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=6.0.6"></script>



  



	





  





  










  





  

  

  

  

  
  

  

  

  

  


<script src="/live2dw/lib/L2Dwidget.min.js?0c58a1486de42ac6cc1c59c7d98ae887"></script><script>L2Dwidget.init({"display":{"position":"right","width":100,"height":150},"model":{"jsonPath":"/live2dw/assets/tororo.model.json"},"mobile":{"show":false,"scale":0.5},"react":{"opacityDefault":1,"opacityOnHover":0.2}});</script></body>
</html>
